package cn.haiway.drools.execute.service;

import cn.haiway.drools.execute.dto.ParamDTO;
import cn.haiway.drools.utils.KJarDefineConstant;
import cn.haiway.drools.utils.Reflections;
import lombok.extern.slf4j.Slf4j;
import org.drools.compiler.kie.builder.impl.InternalKieModule;
import org.kie.api.KieServices;
import org.kie.api.builder.KieRepository;
import org.kie.api.builder.ReleaseId;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.rule.FactHandle;
import org.kie.internal.io.ResourceFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;

/**
 * @author haiway Date : 2019/5/12 0012
 */
@Service
@Slf4j
public class DroolsExecuteService {

    @Resource
    KieServices kieServices;

    @Resource
    RedisService redisService;

    /**
     * 更新drools规则
     * @param groupId
     * @param artfactId
     * @param version
     * @return
     */
    public KieContainer updateKieBase(String groupId,String artfactId,String version){
        ReleaseId releaseId = kieServices.newReleaseId(groupId,artfactId,version);

        KieRepository repository = kieServices.getRepository();

        byte[] kjar = redisService.getKjar(groupId,artfactId,version);
        if(kjar==null){
            log.error("不存在对应的规则");
        }

        repository.addKieModule(ResourceFactory.newByteArrayResource(kjar));

        KieContainer kieContainer = kieServices.newKieContainer(releaseId);

        kieContainer.updateToVersion(releaseId);
        return kieContainer;
    }


    public Map<String,Object> executeRule(ParamDTO paramDTO) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        long start = System.currentTimeMillis();
        ReleaseId releaseId = kieServices.newReleaseId(paramDTO.getProject().getGroupId(), paramDTO.getProject().getArtifactId(), paramDTO.getProject().getVersion());

        KieContainer kieContainer = null;

        InternalKieModule kieModule = (InternalKieModule) kieServices.getRepository().getKieModule(releaseId);
        if (kieModule == null) {
            log.warn("Drools 中不存在KieModule,对应的ReleaseId:" + releaseId);
            log.info("尝试从redis中加载....");

            kieContainer = updateKieBase(paramDTO.getProject().getGroupId(), paramDTO.getProject().getArtifactId(), paramDTO.getProject().getVersion());
        } else {
            kieContainer = kieServices.newKieContainer(releaseId);
        }

        KieSession kieSession = kieContainer.newKieSession();

        Map<String, Map<String, Object>> factMap = paramDTO.getFacts();
        Map<String, Object> variables = paramDTO.getVariables();

        //设置全局变量
        if (variables != null && !variables.isEmpty()) {
            for (Map.Entry<String, Object> entry : variables.entrySet()) {
                kieSession.setGlobal(entry.getKey(), entry.getValue());
            }
        }
        //设置输出对象
        Map<String, Object> resultMap = new HashMap<>(200);
        kieSession.setGlobal(KJarDefineConstant.RESULT, resultMap);

        //反射JAVA对象
        ClassLoader classLoader = kieContainer.getClassLoader();
        for (Map.Entry<String, Map<String, Object>> fact : factMap.entrySet()) {
            String clsName = paramDTO.getProject().getGroupId() + KJarDefineConstant.FACT + "." + fact.getKey();
            Object aClass = classLoader.loadClass(clsName).newInstance();

            for (Map.Entry<String, Object> entry : fact.getValue().entrySet()) {
                try {
                    Reflections.invokeSetter(aClass, entry.getKey(), entry.getValue());
                } catch (IllegalArgumentException e) {
                    log.error("参数非法");
                }
            }

            FactHandle handle = kieSession.insert(aClass);

        }

        int count = kieSession.fireAllRules();

        kieSession.dispose();
        long end = System.currentTimeMillis();
        log.info("规则[" + paramDTO + "]调用执行结束==>>共触发规则个数:" + count + ",共用时：" + (end - start) + "ms");

        return resultMap;
    }
}
